#ifndef NET_TCPCLIENT_H_
#define NET_TCPCLIENT_H_
#include <fermat/common/noncopyable.h>
#include <fermat/common/mutex.h>
#include <fermat/common/scoped_lock.h>
#include <fermat/net/tcp_connection.h>
#include <fermat/net/socket_address.h>
#include <fermat/event/event_loop.h>
#include <memory>
#include <string>

namespace fermat {
namespace net{

class Connector;
typedef std::shared_ptr<Connector> ConnectorPtr;

class TcpClient : fermat::noncopyable {
public:
    TcpClient(fermat::event::EventLoopPtr loop,
               const SocketAddress& serverAddr,
              const std::string& name);
    ~TcpClient();  // force out-line dtor, for scoped_ptr members.

    void connect();
    void disconnect();
    void stop();

    TcpConnectionPtr connection() 
    {
      ScopedMutex lock(&_mutex);
      return _connection;
    }

    fermat::event::EventLoopPtr getLoop() const { return _loop; }
    bool retry() const;
    void enableRetry() { _retry = true; }

    /// Set connection callback.
    /// Not thread safe.
    void setConnectionCallback(const ConnectionCallback& cb)
    { _connectionCallback = cb; }

    /// Set message callback.
    /// Not thread safe.
    void setMessageCallback(const MessageCallback& cb)
    { _messageCallback = cb; }

    /// Set write complete callback.
    /// Not thread safe.
    void setWriteCompleteCallback(const WriteCompleteCallback& cb)
    { _writeCompleteCallback = cb; }

private:
    /// Not thread safe, but in loop
    void newConnection(int sockfd);
    /// Not thread safe, but in loop
    void removeConnection(const TcpConnectionPtr& conn);

    fermat::event::EventLoopPtr _loop;
    SocketAddress               _serveraddr;
    ConnectorPtr                _connector; // avoid revealing Connector
    const std::string           _name;
    ConnectionCallback          _connectionCallback;
    MessageCallback             _messageCallback;
    WriteCompleteCallback       _writeCompleteCallback;
    bool                        _retry;   // atomic
    bool                        _connect; // atomic
    // always in loop thread
    int                        _nextConnId;
    Mutex                      _mutex;
    TcpConnectionPtr           _connection; // @GuardedBy mutex_
};
}
}

#endif
